﻿using System;
using System.Linq;
using System.Collections.Generic;
using CacheNaga.HashAlgorithms;

namespace CacheNaga
{
	internal delegate T UseSocket<T>(PooledSocket socket);
	internal delegate void UseSocket(PooledSocket socket);

	/// <summary>
	/// 封装了memcached server和其socketPool对象
	/// 该类负责指示缓存对象存储在哪台服务器上
	/// </summary>
	internal class ServerPool
	{
		private static LogAdapter logger = LogAdapter.GetLogger(typeof(ServerPool));

		private SocketPool[] hostList;
		internal SocketPool[] HostList { get { return hostList; } }

		private Dictionary<uint, SocketPool> hostDictionary;
		private uint[] hostKeys;

		//默认配置属性
		private int sendReceiveTimeout = 2000;//发送和接受数据超时时间
		private uint maxPoolSize = 6;//pool最大值
		private uint minPoolSize = 5;//pool最小值
		private TimeSpan socketRecycleAge = TimeSpan.FromMinutes(30);//socket回收时间

		/// <summary>
		/// 默认超时时间
		/// </summary>
		internal int SendReceiveTimeout { get { return sendReceiveTimeout; } set { sendReceiveTimeout = value; } }
		/// <summary>
		/// 最大Pool大小
		/// </summary>
		internal uint MaxPoolSize { get { return maxPoolSize; } set { maxPoolSize = value; } }
		/// <summary>
		/// 最小Pool大小
		/// </summary>
		internal uint MinPoolSize { get { return minPoolSize; } set { minPoolSize = value; } }
		/// <summary>
		/// Socket回收时间
		/// </summary>
		internal TimeSpan SocketRecycleAge { get { return socketRecycleAge; } set { socketRecycleAge = value; } }

		/// <summary>
		/// 创建consistant hasing结构
		/// </summary>
		internal ServerPool(string[] hosts)
		{
			//哈希圆环上的SocketPool的分布点
			hostDictionary = new Dictionary<uint, SocketPool>();
			//哈希圆环上的SocketPool
			List<SocketPool> pools = new List<SocketPool>();
			List<uint> keys = new List<uint>();
			foreach (string host in hosts)
			{
				//每个memcached server配置一个SocketPool
				SocketPool pool = new SocketPool(this, host.Trim());
				//为每个SocketPool创建250个节点
				for (int i = 0; i < 250; i++)
				{
					//停用默认的FNV算法
					//uint key = BitConverter.ToUInt32(new ModifiedFNV1_32().ComputeHash(Encoding.UTF8.GetBytes(host + "-" + i)), 0);
					//使用改进的MD5算法
					uint key = EnhancedMD5HashAlgorithm.Md5(host + "-" + i.ToString());
					if (!hostDictionary.ContainsKey(key))
					{
						hostDictionary[key] = pool;
						keys.Add(key);
					}
				}

				pools.Add(pool);
			}
			hostList = pools.ToArray();

			//排序所有的keys
			keys.Sort();
			hostKeys = keys.ToArray();
		}

		/// <summary>
		/// ketama算法 计算一个缓存项的key在hash后应该路由到哪台服务器
		/// </summary>
		internal SocketPool GetSocketPool(uint hash)
		{
			//如果只使用一台memcached client的话直接返回
			if (hostList.Length == 1)
			{
				return hostList[0];
			}

			//使用ketama算法选择memcached server
			int i = Array.BinarySearch(hostKeys, hash);
			//如果hash不再hostKeys上
			if (i < 0)
			{
				//找到第一个大于hash的hostKeys的位置
				//如果hostKeys为[1,2,4,5], hash为3,则i=-2,~i=3
				i = ~i;
				//如果hash不在hostKeys的范围内,指向第一台memcached server
				//如果hostKeys为[1,2,4,5], hash为6,则~i>4
				if (i >= hostKeys.Length) i = 0;
			}
			var selected = hostDictionary[hostKeys[i]];
			return selected;
		}

		/// <summary>
		/// 根据host查找SocketPool并返回
		/// </summary>
		/// <param name="host"></param>
		/// <returns></returns>
		internal SocketPool GetSocketPool(string host)
		{
			return HostList.Where(p => p.Host == host).FirstOrDefault();
			//return Array.Find(HostList, delegate(SocketPool socketPool) { return socketPool.Host == host; });
		}

		/// <summary>
		/// 根据hash找到相对应的server,在socket上执行相应的方法
		/// 如果执行出错返回类型T的defaultvalue
		/// </summary>
		internal T Execute<T>(uint hash, T defaultValue, UseSocket<T> use)
		{
			return Execute(GetSocketPool(hash), defaultValue, use);
		}

		internal T Execute<T>(SocketPool pool, T defaultValue, UseSocket<T> use)
		{
			PooledSocket sock = null;
			try
			{
				//获取可用的池
				sock = pool.Acquire();

				//将socket作为参数传递执行委托
				if (sock != null)
				{
					return use(sock);
				}
			}
			catch (Exception e)
			{
				logger.Error("Error in Execute<T>: " + pool.Host, e);

				//socket挂了的话关闭
				if (sock != null)
				{
					sock.Close();
				}
			}
			finally
			{
				if (sock != null)
				{
					sock.Dispose();
				}
			}
			return defaultValue;
		}

		internal void Execute(SocketPool pool, UseSocket use)
		{
			PooledSocket sock = null;
			try
			{
				sock = pool.Acquire();
				if (sock != null)
				{
					use(sock);
				}
			}
			catch (Exception e)
			{
				logger.Error("Error in Execute: " + pool.Host, e);
				if (sock != null)
				{
					sock.Close();
				}
			}
			finally
			{
				if (sock != null)
				{
					sock.Dispose();
				}
			}
		}

		/// <summary>
		/// 在所有的服务器上都执行该委托
		/// </summary>
		internal void ExecuteAll(UseSocket use)
		{
			foreach (SocketPool socketPool in hostList)
			{
				Execute(socketPool, use);
			}
		}
	}
}